%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% https://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ Management Plugin.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2018 Pivotal Software, Inc.  All rights reserved.
%%

-module(rabbit_mgmt_wm_overview).

-export([init/2]).
-export([to_json/2, content_types_provided/2, is_authorized/2]).
-export([variances/2]).

-import(rabbit_misc, [pget/2, pget/3]).

-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
-include_lib("rabbit_common/include/rabbit.hrl").

%%--------------------------------------------------------------------

init(Req, _State) ->
    {cowboy_rest, rabbit_mgmt_headers:set_common_permission_headers(Req, ?MODULE), #context{}}.

variances(Req, Context) ->
    {[<<"accept-encoding">>, <<"origin">>], Req, Context}.

content_types_provided(ReqData, Context) ->
   {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.

to_json(ReqData, Context = #context{user = User = #user{tags = Tags}}) ->
    RatesMode = rabbit_mgmt_agent_config:get_env(rates_mode),
    SRP = get_sample_retention_policies(),
    %% NB: this duplicates what's in /nodes but we want a global idea
    %% of this. And /nodes is not accessible to non-monitor users.
    ExchangeTypes = rabbit_mgmt_external_stats:list_registry_plugins(exchange),
    Overview0 = [{management_version,        version(rabbitmq_management)},
                 {rates_mode,                RatesMode},
                 {sample_retention_policies, SRP},
                 {exchange_types,            ExchangeTypes},
                 {rabbitmq_version,          version(rabbit)},
                 {cluster_name,              rabbit_nodes:cluster_name()},
                 {erlang_version,            erlang_version()},
                 {erlang_full_version,       erlang_full_version()}],
    try
        Range = rabbit_mgmt_util:range(ReqData),
        Overview =
            case rabbit_mgmt_util:is_monitor(Tags) of
                true ->
                    Overview0 ++
                        [{K, maybe_map(V)} ||
                            {K,V} <- rabbit_mgmt_db:get_overview(Range)] ++
                        [{node,               node()},
                         {listeners,          listeners()},
                         {contexts,           web_contexts(ReqData)}];
                _ ->
                    Overview0 ++
                        [{K, maybe_map(V)} ||
                            {K, V} <- rabbit_mgmt_db:get_overview(User, Range)]
            end,
        rabbit_mgmt_util:reply(Overview, ReqData, Context)
    catch
        {error, invalid_range_parameters, Reason} ->
            rabbit_mgmt_util:bad_request(iolist_to_binary(Reason), ReqData, Context)
    end.

is_authorized(ReqData, Context) ->
    rabbit_mgmt_util:is_authorized(ReqData, Context).

%%--------------------------------------------------------------------

version(App) ->
    {ok, V} = application:get_key(App, vsn),
    list_to_binary(V).

listeners() ->
    rabbit_mgmt_util:sort_list(
      [rabbit_mgmt_format:listener(L)
       || L <- rabbit_networking:active_listeners()],
      ["protocol", "port", "node"] ).

maybe_map(L) when is_list(L) -> maps:from_list(L);
maybe_map(V)                 -> V.

%%--------------------------------------------------------------------

web_contexts(ReqData) ->
    rabbit_mgmt_util:sort_list(
      lists:append(
        [fmt_contexts(N) || N <- rabbit_mgmt_wm_nodes:all_nodes(ReqData)]),
      ["description", "port", "node"]).

fmt_contexts(Node) ->
    [fmt_context(Node, C) || C <- pget(contexts, Node, [])].

fmt_context(Node, C) ->
  rabbit_mgmt_format:web_context([{node, pget(name, Node)} | C]).

erlang_version() -> list_to_binary(rabbit_misc:otp_release()).

erlang_full_version() ->
    list_to_binary(rabbit_misc:otp_system_version()).

get_sample_retention_policies() ->
    P = rabbit_mgmt_agent_config:get_env(sample_retention_policies),
    get_sample_retention_policies(P).

get_sample_retention_policies(undefined) ->
    [{global, []}, {basic, []}, {detailed, []}];
get_sample_retention_policies(Policies) ->
    [transform_retention_policy(Pol, Policies) || Pol <- [global, basic, detailed]].

transform_retention_policy(Pol, Policies) ->
    case proplists:lookup(Pol, Policies) of
        none ->
            {Pol, []};
        {Pol, Intervals} ->
            {Pol, transform_retention_intervals(Intervals, [])}
    end.

transform_retention_intervals([], Acc) ->
    lists:sort(Acc);
transform_retention_intervals([{MaxAgeInSeconds, _}|Rest], Acc) ->
    %
    % Seconds | Interval
    % 60      | last minute
    % 600     | last 10 minutes
    % 3600    | last hour
    % 28800   | last 8 hours
    % 86400   | last day
    %
    % rabbitmq/rabbitmq-management#635
    %
    % We check for the max age in seconds to be within 10% of the value above.
    % The reason being that the default values are "bit higher" to accommodate
    % edge cases (see deps/rabbitmq_management_agent/Makefile)
    AccVal = if
                 MaxAgeInSeconds >= 0 andalso MaxAgeInSeconds =< 66 ->
                     60;
                 MaxAgeInSeconds >= 540 andalso MaxAgeInSeconds =< 660 ->
                     600;
                 MaxAgeInSeconds >= 3240 andalso MaxAgeInSeconds =< 3960 ->
                     3600;
                 MaxAgeInSeconds >= 25920 andalso MaxAgeInSeconds =< 31681 ->
                     28800;
                 MaxAgeInSeconds >= 77760 andalso MaxAgeInSeconds =< 95041 ->
                     86400;
                 true ->
                     0
             end,
    transform_retention_intervals(Rest, [AccVal|Acc]).
